; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=inline < %s -S -o - | FileCheck %s
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.15.0"

define void @caller1(ptr %p1, i1 %b) {
; CHECK-LABEL: @caller1(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i1 [[B:%.*]], true
; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
; CHECK:       split:
; CHECK-NEXT:    call void @callee(ptr [[P1:%.*]], i32 0, i32 -1)
; CHECK-NEXT:    br label [[EXIT]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %cond = icmp eq i1 %b, true
  br i1 %cond, label %exit, label %split

split:
  ; This path may be generated from CS splitting and never taken at runtime.
  call void @callee(ptr %p1, i32 0, i32 -1)
  br label %exit

exit:
  ret void
}

define  void @callee(ptr %p1, i32 %l1, i32 %l2) {
entry:
  %ext = zext i32 %l2 to i64
  %vla = alloca float, i64 %ext, align 16
  call void @extern_call(ptr nonnull %vla) #3
  ret void
}


define void @caller2_below_threshold(ptr %p1, i1 %b) {
; CHECK-LABEL: @caller2_below_threshold(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[VLA_I:%.*]] = alloca float, i64 15000, align 16
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i1 [[B:%.*]], true
; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
; CHECK:       split:
; CHECK-NEXT:    [[SAVEDSTACK:%.*]] = call ptr @llvm.stacksave()
; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 60000, ptr [[VLA_I]])
; CHECK-NEXT:    call void @extern_call(ptr nonnull [[VLA_I]]) #3
; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 60000, ptr [[VLA_I]])
; CHECK-NEXT:    call void @llvm.stackrestore(ptr [[SAVEDSTACK]])
; CHECK-NEXT:    br label [[EXIT]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %cond = icmp eq i1 %b, true
  br i1 %cond, label %exit, label %split

split:
  call void @callee(ptr %p1, i32 0, i32 15000)
  br label %exit

exit:
  ret void
}

define  void @callee2_not_in_entry(ptr %p1, i32 %l1, i32 %l2) {
entry:
  %ext = zext i32 %l2 to i64
  %c = icmp eq i32 %l1, 42
  br i1 %c, label %bb2, label %bb3
bb2:
  %vla = alloca float, i64 %ext, align 16
  call void @extern_call(ptr nonnull %vla) #3
  ret void
bb3:
  ret void
}

define void @caller3_alloca_not_in_entry(ptr %p1, i1 %b) {
; CHECK-LABEL: @caller3_alloca_not_in_entry(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i1 [[B:%.*]], true
; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
; CHECK:       split:
; CHECK-NEXT:    br label [[EXIT]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %cond = icmp eq i1 %b, true
  br i1 %cond, label %exit, label %split

split:
  call void @callee2_not_in_entry(ptr %p1, i32 0, i32 -1)
  br label %exit

exit:
  ret void
}

define void @caller4_over_threshold(ptr %p1, i1 %b, i32 %len) {
; CHECK-LABEL: @caller4_over_threshold(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i1 [[B:%.*]], true
; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
; CHECK:       split:
; CHECK-NEXT:    call void @callee(ptr [[P1:%.*]], i32 0, i32 16500)
; CHECK-NEXT:    br label [[EXIT]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %cond = icmp eq i1 %b, true
  br i1 %cond, label %exit, label %split

split:
  call void @callee(ptr %p1, i32 0, i32 16500)
  br label %exit

exit:
  ret void
}

declare noalias ptr @malloc(i64)
define ptr @stack_allocate(i32 %size) #2 {
entry:
  %cmp = icmp ult i32 %size, 100
  %conv = zext i32 %size to i64
  br i1 %cmp, label %if.then, label %if.end

if.then:                                          ; preds = %entry
  %0 = alloca i8, i64 %conv, align 8
  br label %return

if.end:                                           ; preds = %entry
  %call = tail call ptr @malloc(i64 %conv) #3
  br label %return

return:                                           ; preds = %if.end, %if.then
  %retval.0 = phi ptr [ %0, %if.then ], [ %call, %if.end ]
  ret ptr %retval.0
}

define ptr @test_stack_allocate_always(i32 %size) {
; CHECK-LABEL: @test_stack_allocate_always(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[SAVEDSTACK:%.*]] = call ptr @llvm.stacksave()
; CHECK-NEXT:    [[CMP_I:%.*]] = icmp ult i32 [[SIZE:%.*]], 100
; CHECK-NEXT:    [[CONV_I:%.*]] = zext i32 [[SIZE]] to i64
; CHECK-NEXT:    br i1 [[CMP_I]], label [[IF_THEN_I:%.*]], label [[IF_END_I:%.*]]
; CHECK:       if.then.i:
; CHECK-NEXT:    [[TMP0:%.*]] = alloca i8, i64 [[CONV_I]], align 8
; CHECK-NEXT:    br label [[STACK_ALLOCATE_EXIT:%.*]]
; CHECK:       if.end.i:
; CHECK-NEXT:    [[CALL_I:%.*]] = tail call ptr @malloc(i64 [[CONV_I]]) #3
; CHECK-NEXT:    br label [[STACK_ALLOCATE_EXIT]]
; CHECK:       stack_allocate.exit:
; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], [[IF_THEN_I]] ], [ [[CALL_I]], [[IF_END_I]] ]
; CHECK-NEXT:    call void @llvm.stackrestore(ptr [[SAVEDSTACK]])
; CHECK-NEXT:    ret ptr [[RETVAL_0_I]]
;
entry:
  %call = tail call ptr @stack_allocate(i32 %size)
  ret ptr %call
}

declare void @extern_call(ptr)

attributes #1 = { argmemonly nounwind willreturn writeonly }
attributes #2 = { alwaysinline }
attributes #3 = { nounwind }

